package service

import (
	"context"
	"encoding/json"
	"errors"
	"github.com/apache/rocketmq-clients/golang/v5"
	pb "goods-srv/gen/goods"
	"log"
	"order-srv/internal/biz/dto"
	"order-srv/internal/biz/service/client"
	"order-srv/internal/dao"
	"order-srv/internal/model"
	"order-srv/libs"
	"strconv"
)

type IOrder interface {
	CreateOrder(ctx context.Context, createOrder dto.CreateOrder) (orderId string, err error)
}

type Order struct {
	orderDao dao.IOrder
	producer golang.Producer
}

func (s Order) CreateOrder(ctx context.Context, createOrder dto.CreateOrder) (orderId string, err error) {

	// 一：创建订单
	var order model.Order
	var orderGoodsSkuList []model.OrderGoodsSku
	libs.MappingStruct(createOrder, &order)

	// 这里实际是查商品信息，计算价格，校验库存
	for _, v := range createOrder.OrderGoodsSku {
		orderGoodsSku := model.OrderGoodsSku{}
		libs.MappingStruct(v, &orderGoodsSku)
		orderGoodsSkuList = append(orderGoodsSkuList, orderGoodsSku)
	}

	id, err := s.orderDao.CreateOrder(order, orderGoodsSkuList)
	if err != nil {
		return orderId, err
	}
	orderId = strconv.FormatUint(id, 10)

	// 二：订单创建成功，发送半消息
	// 1.创建消息
	var goodsSkuListMessage pb.GoodsSkuListMessage
	goodsSkuListMessage.OrderId = orderId
	for _, v := range createOrder.OrderGoodsSku {
		goodsSkuListMessage.List = append(goodsSkuListMessage.List, &pb.GoodsSkuMessage{Id: v.GoodsSkuId, Stock: uint64(v.Count)})
	}

	//orderMsg.OrderId = orderId
	body, err := json.Marshal(goodsSkuListMessage)
	if err != nil {
		return orderId, err
	}

	// 开启MQ事务
	transaction := s.producer.BeginTransaction()
	msg := &golang.Message{
		Topic: libs.TransactionTopic,
		Body:  body, // 订单创建成功，发送半消息
	}
	// set keys and tag
	//msg.SetKeys("orderId", "b")
	msg.SetTag("order")
	resp, err := s.producer.SendWithTransaction(context.TODO(), msg, transaction)
	if err != nil {
		return orderId, err
	}
	for i := 0; i < len(resp); i++ {
		log.Printf("%#v\n", resp[i])
	}

	// 三：执行扣减成功
	// 创建一个子span
	span := libs.CreateChildSpan("DeductionStock", ctx)
	_, err = client.GoodsClient.DeductionStock(context.Background(), &goodsSkuListMessage)
	span.Finish() // *前面不能加 defer
	// 扣减库存失败
	if err != nil {
		// 取消归还库存半消息
		if rollBackErr := transaction.RollBack(); rollBackErr != nil {
			return orderId, errors.New("扣减库存失败，回滚消息失败")
		}
		return orderId, err
	}

	// 调用物流服务，创建物流信息，下单失败
	if err = errors.New("物流信息创建失败"); err != nil {
		// 发送确认消息，执行归还库存
		log.Println("发送确认消息，执行归还库存")
		if commitErr := transaction.Commit(); commitErr != nil {
			log.Println(err.Error(), "发送确认消息，执行归还库存Commit错误")
			return orderId, commitErr
		}
		return orderId, err
	}

	// 以上逻辑全部执行成功，更新订为下单成功状态
	if err = s.orderDao.UpDateOrder(id, model.Order{Status: 1}); err != nil {
		return orderId, errors.New("更新订单状态失败")
	}

	// 最后：整个下单成功，取消归还库存半消息
	if rollBackErr := transaction.RollBack(); rollBackErr != nil {
		return orderId, errors.New("扣减库存失败，回滚消息失败")
	}

	return orderId, nil
}

func NewOrder() IOrder {
	orderDao := dao.NewOrder()

	// 创建服务订单业务的事务消息
	producer, err := libs.NewTransactionProducer(&golang.TransactionChecker{
		Check: func(msg *golang.MessageView) golang.TransactionResolution {
			//log.Printf("check transaction message: %v", msg)
			// 反序列化消息
			var goodsSkuListMessage pb.GoodsSkuListMessage
			if err := json.Unmarshal(msg.GetBody(), &goodsSkuListMessage); err != nil {
				return golang.UNKNOWN
			}
			// 查询订单状态，根据订单状态处理业务
			intNum, _ := strconv.Atoi(goodsSkuListMessage.OrderId)
			order, err := orderDao.SelectOrderById(uint64(intNum))
			if err != nil {
				return golang.UNKNOWN
			}
			// 1 下单成功，待支付
			if order.Status == 1 {
				// 订单创建成功，半消息就不用了
				return golang.ROLLBACK
			} else {
				// 这个订单创建失败就提交半消息，商品服务执行归还库存
				return golang.COMMIT
			}
		},
	})
	if err != nil {
		panic(err.Error())
	}

	err = producer.Start()
	if err != nil {
		panic(err.Error())
	}

	libs.Signal(func(err error) {
		if err != nil {
			// mq 优雅退出
			if err = producer.GracefulStop(); err != nil {
				log.Fatal(err.Error())
			}
		}
	})

	return Order{
		orderDao: orderDao,
		producer: producer,
	}
}
